interface TbConfig {
  name: string;
  autoIncrement?: boolean;
  keyPath?: string;
  indexs?: IndexConfig[];
}
interface IndexConfig {
  name: string;
  prop: string;
  config?: { unique: boolean };
}

class IndexdbHelper {
  public db?: IDBDatabase;

  public haveIndex = false;

  public canuseIndex = false;

  public databaseName?: string;

  public dbVersion?: number;

  public tbConfigs: TbConfig[] = [];

  constructor(
    databaseName: string,
    tbConfigs?: TbConfig[],
    dbVersion?: number,
  ) {
    this.databaseName = databaseName;
    this.dbVersion = dbVersion || 2;
    this.tbConfigs = tbConfigs || [];
    if (window.indexedDB) {
      this.haveIndex = true;
    } else this.haveIndex = false;
  }

  // indexdb 初始化
  ready(): Promise<IDBDatabase | null> {
    if (this.haveIndex) {
      if (this.db) return Promise.resolve(this.db);
      return new Promise(async (resolve, reject) => {
        const request = window.indexedDB.open(
          this.databaseName as string,
          this.dbVersion,
        );
        request.onerror = () => {
          console.log('数据库打开报错');
          this.canuseIndex = false;
          reject();
        };
        request.onsuccess = async () => {
          this.db = request.result;
          console.log('数据库打开成功');
          this.canuseIndex = true;
          resolve(this.db);
        };
        request.onupgradeneeded = (event) => {
          this.canuseIndex = true;
          this.db = (event.target as any).result as IDBDatabase;
          this.initTb();
          const transaction = (event.target as any).transaction;
          transaction.oncomplete = function () {
            resolve(this.db);
          };
        };
      });
    }
    return Promise.resolve(null);
  }

  // 创建表
  createStore(
    tbName: string,
    tconfig?: Omit<TbConfig, 'name'>,
  ): IDBObjectStore | undefined {
    if (!this.canuseIndex || !this.haveIndex) return;
    let objectStore: IDBObjectStore | undefined;
    if (!this.db?.objectStoreNames.contains(tbName)) {
      const { keyPath, indexs = [], ...config } = tconfig || {};
      let key = keyPath;
      let indexList = indexs;
      if (!key) {
        key = 'id';
        config.autoIncrement = true;
        if (!indexList.length) {
          indexList = [{ name: 'id', prop: 'id', config: { unique: true } }];
        }
      }
      objectStore = this.db?.createObjectStore(tbName, {
        ...config,
        keyPath: key,
      });
      indexList.forEach((index) => this.createIndex(objectStore, index));
    }
    return objectStore;
  }

  // 创建index
  createIndex(tb: string | IDBObjectStore | undefined, index: IndexConfig) {
    if (!tb || !this.db) return false;
    let sotre: IDBObjectStore;
    if (typeof tb === 'string') {
      sotre = this.db.transaction(tb).objectStore(tb);
    } else {
      sotre = tb;
    }
    const { name, prop, config } = index;
    sotre.createIndex(name, prop, config);
    return true;
  }

  // 删除表中所有数据
  dropStore(tbName: string) {
    return new Promise((resolve, reject) => {
      if (!this.canuseIndex || !this.db) return reject();
      const objectStore = this.db.transaction(tbName).objectStore(tbName);
      const request = objectStore.openCursor();
      request.onsuccess = function (event) {
        const cursor = (event.target as any).result;
        if (cursor) {
          cursor.delete();
        } else {
          resolve(true);
        }
      };
      request.onerror = function () {
        reject();
      };
    });
  }

  // 插入数据
  create(tbName: string, data: any) {
    return new Promise(async (resolve, reject) => {
      if (!this.canuseIndex || !this.db) return reject();
      const keyPath = this.db
        .transaction([tbName], 'readwrite')
        .objectStore(tbName).keyPath;
      const isExist = await this.findByPk(tbName, data[keyPath as string]);
      if (isExist) resolve(true);
      const request = this.db
        .transaction([tbName], 'readwrite')
        .objectStore(tbName)
        .add(data);
      request.onsuccess = function () {
        resolve(data);
      };

      request.onerror = function () {
        reject();
      };
    });
  }

  bulkCreate(tbName: string, data: any[]) {
    return new Promise(async (resolve, reject) => {
      if (!this.canuseIndex || !this.db) return reject();
      if (!Array.isArray(data) || data.length === 0) return resolve(true);
      const retList = await Promise.all(
        data.map(async (d) => await this.create(tbName, d)),
      );
      if (retList.every((t) => !!t)) resolve(true);
      else reject();
    });
  }

  // 根据主键查找
  findByPk(tbName: string, keyPath: string) {
    return new Promise((resolve, reject) => {
      if (!this.canuseIndex || !this.db) return reject();
      const transaction = this.db.transaction([tbName]);
      const objectStore = transaction.objectStore(tbName);
      const request = objectStore.get(keyPath);
      request.onerror = function () {
        reject();
      };

      request.onsuccess = function () {
        if (request.result) {
          resolve(request.result);
        } else {
          resolve(null);
        }
      };
    });
  }

  // 根据索引查找
  findByIndex(tbName: string, indexName: string, keyPath: string) {
    return new Promise((resolve, reject) => {
      if (!this.canuseIndex || !this.db) return reject();
      const request = this.db
        .transaction([tbName])
        .objectStore(tbName)
        .index(indexName)
        .get(keyPath);
      request.onerror = function () {
        reject();
      };

      request.onsuccess = function (e) {
        const result = (e.target as any).result;
        if (result) {
          resolve(result);
        } else {
          resolve(null);
        }
      };
    });
  }

  //   遍历指定表
  query(tbName: string, filter?: (item: any) => boolean) {
    return new Promise((resolve, reject) => {
      const list: any[] = [];
      if (!this.canuseIndex || !this.db) return reject();
      const objectStore = this.db.transaction(tbName).objectStore(tbName);
      const request = objectStore.openCursor();
      request.onsuccess = function (event) {
        const cursor = (event.target as any).result;
        if (cursor) {
          if (!filter || filter(cursor.value)) list.push(cursor.value);
          cursor.continue();
        } else {
          resolve(list);
        }
      };
      request.onerror = function () {
        reject();
      };
    });
  }

  //   更新指定数据
  update(tbName: string, data: any) {
    return new Promise((resolve, reject) => {
      if (!this.canuseIndex || !this.db) return reject();
      const request = this.db
        .transaction([tbName], 'readwrite')
        .objectStore(tbName)
        .put(data);
      request.onsuccess = function () {
        resolve(data);
      };
      request.onerror = function () {
        reject();
      };
    });
  }

  // 删除数据
  destroy(tbName: string, keyPath: string) {
    return new Promise((resolve, reject) => {
      if (!this.canuseIndex || !this.db) return reject();
      const request = this.db
        .transaction([tbName], 'readwrite')
        .objectStore(tbName)
        .delete(keyPath);
      request.onsuccess = function () {
        resolve(true);
      };
      request.onerror = function (e) {
        reject(e);
      };
    });
  }

  initTb() {
    this.tbConfigs.forEach(({ name, ...config }) => {
      this.createStore(name, config);
    });
  }
}

export default IndexdbHelper;
